﻿/*******************************************************************************************
 * Copyright (c) 2006-2009 Alex Robson, Rob Simmons, Craig Israel, and Rachel Twyford
 *
 * Use of this software in any form requires that you have both 
 * read and agreed to the following terms:
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions: 
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, 
 * this list of conditions and the following disclaimer in the 
 * documentation and/or other materials provided with the distribution.
 * Neither the name of Nvigorate nor the names of its contributors may 
 * be used to endorse or promote products derived from this software without 
 * specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL ANY CONTRIBUTOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ****************************************************************************************/

using System;
using System.Xml.Linq;
using Nvigorate.Relational.Mapping.Interface;
using Nvigorate.Xml;

namespace Nvigorate.Relational.Mapping
{
    [Serializable]
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
    public abstract class Assignment : Attribute, IAssignment, IEquatable<Assignment>
    {
        protected IAssignmentPoint _source;
        protected IAssignmentPoint _target;
        protected bool _nullable;
        protected bool _persistable = true;
        private string _sourceEntity = "";
        private string _targetEntity = "";
        private int _argumentIndex;
        private bool _isConstructorArgument;
        private Type _constructedType;

        #region IAssignment Members

        private string _constructedProperty;

        public IAssignmentPoint Source
        {
            get { return _source; }
            set { _source = value; }
        }

        public IAssignmentPoint Target
        {
            get { return _target; }
            set { _target = value; }
        }

        public bool PrimaryKey { get; set; }

        public bool ForeignKey { get; set; }

        public bool Nullable
        {
            get { return _nullable; }
            set { _nullable = value; }
        }

        public bool Persistable
        {
            get { return _persistable; }
            set { _persistable = value; }
        }

        public int ArgumentIndex
        {
            get { return _argumentIndex; }
            set { _argumentIndex = value; }
        }

        public bool IsConstructorArgument
        {
            get { return _isConstructorArgument; }
            set { _isConstructorArgument = value; }
        }

        public Type ConstructedType
        {
            get { return _constructedType; }
            set { _constructedType = value; }
        }

        public string ConstructedProperty
        {
            get { return _constructedProperty; }
            set { _constructedProperty = value; }
        }

        #endregion

        #region Implementation of ILinqToXml

        public XElement GetXElementRepresentation()
        {
            var element = new XElement("assignment", 
                                       new XAttribute("source", _source.Field),
                                       new XAttribute("target", _target.Field),
                                       new XAttribute("primaryKey", PrimaryKey),
                                       new XAttribute("foreignKey", ForeignKey),
                                       new XAttribute("persistable", _persistable),
                                       new XAttribute("nullable", _nullable)
                );
            return element;
        }

        public object ParseXElement(XElement element)
        {
            _source = new AssignmentPoint()
                          {
                              Entity = _sourceEntity,
                              Field = element.GetAttributeValue("source") ?? ""
                          };

            _target = new AssignmentPoint()
                          {
                              Entity = _targetEntity,
                              Field = element.GetAttributeValue("target") ?? ""
                          };

            _persistable = bool.Parse(element.GetAttributeValue("persistable") ?? "true");
            _nullable = bool.Parse(element.GetAttributeValue("nullable") ?? "false");

            return this;
        }

        #endregion

        #region Equality Members

        public bool Equals(Assignment obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            return base.Equals(obj) && Equals(obj._source, _source) && Equals(obj._target, _target) && obj._persistable.Equals(_persistable) && obj._nullable.Equals(_nullable);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            return Equals(obj as Assignment);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                int result = base.GetHashCode();
                result = (result * 397) ^ (_source != null ? _source.GetHashCode() : 0);
                result = (result * 397) ^ (_target != null ? _target.GetHashCode() : 0);
                result = (result * 397) ^ _persistable.GetHashCode();
                result = (result * 397) ^ _nullable.GetHashCode();
                return result;
            }
        }

        #endregion

        public Assignment()
        {
        }

        public Assignment(string sourceEntity, string targetEntity)
        {
            _sourceEntity = sourceEntity;
            _targetEntity = targetEntity;
        }
    }
}